classdef Node < ClockHandler
    %NODE Summary of this class goes here
    %   Detailed explanation goes here
    
    properties (SetAccess = 'public', GetAccess = 'public')
        id = 0;
        x_value;
        y_value;
        is_hap;
        
        % distance to all other nodes distance_vector(i) is the distance to
        % node i from this node
        distance_vector =[]; 
        packets_id_cache=[];
        node_adapter=[];
        node_router = [];
        neighbours = [];
        
        %throughput calculation
        size_of_data_in_bytes_received = 0;
    end
    
    methods
        function obj = Node(id, x_value, y_value, router, is_hap)
            if nargin > 0
                obj.id = id;
                obj.x_value = x_value;
                obj.y_value = y_value;
                obj.node_router = router;
                obj.is_hap = is_hap;
            end
        end
        
        function clear(obj)
            obj.packets_id_cache = [];
        end
        
        function update_received_data_size(obj, received_size_in_bytes)
            obj.size_of_data_in_bytes_received = obj.size_of_data_in_bytes_received+received_size_in_bytes;
        end
        
        function ret = was_packet_cached(obj, packet_ids)
            ret = false;
            for i = 1:length(obj.packets_id_cache)
                for j=1:length(packet_ids)
                    if packet_ids(j) == obj.packets_id_cache(i)
                        ret = true;
                        return;
                    end
                end
            end
        end
        
        function deposit_packets_for_this_tick(obj, packets_vec)
            utils = Utils.instance();
            
            %prepare all packets for transmission
            
            params = ParamsSingleton.instance();
            %check for discardable packets
            for i=1:length(packets_vec)
                curr_multi_packet = packets_vec(i);
                curr_packet = curr_multi_packet.packet;
                curr_packet.add_node_to_packet_route(obj.id);
                if curr_packet.dest_node.id == obj.id
                    packets_manager = PacketsManager.instance();
                    packets_manager.add_packet_to_delivered_packets(curr_multi_packet);
                    curr_packet.discard_packet();
                    continue;
                end
                % ignore if this packet was cached
                if (obj.was_packet_cached(curr_multi_packet.id_history(1:curr_multi_packet.id_history_index-1))) && params.should_romer_use_node_cache_for_packet_ids
                    utils.trace(sprintf('node id %d : packet id %d in cache, discarding\n',obj.id,curr_multi_packet.id));
                    curr_packet.discard_packet();
                    continue;
                end
                PacketLoser.lose_packets(curr_multi_packet);
                if curr_multi_packet.num_of_packets==0
                    curr_packet.discard_packet();
                end
                %if packet should not be discarded, add it to the cahce
                if params.should_romer_use_node_cache_for_packet_ids
                    obj.packets_id_cache = [obj.packets_id_cache curr_multi_packet.id];
                end
            end
            
            %get packets for transmission
            packet_for_transmission = obj.node_router.get_packets_for_transmission(obj, packets_vec);    
            %send to adapter
            if length(packet_for_transmission) > 0
                obj.node_adapter.transmit_packets(packet_for_transmission);
            end
        end
        
        function handle_fully_transmitted_packets(obj, packets)
            utils = Utils.instance();
            utils.trace(sprintf('node [node id %d]: %d packets deposited in this node...calling router\n',obj.id, length(packets)));
            
            %transfer packets to the router for processing
            if ~isempty(packets)
                obj.node_router.route_packets_to_nodes(packets);
            end
        end
        
        function ret = is_node_idle(obj)
            ret = obj.node_adapter.is_adapter_idle();
        end
        
    end
    
      methods(Static)
        function static_clock_tick_handler(obj)
            obj.clock_tick_handler();
        end
      end
end